using System;
using System.Diagnostics;
using System.Configuration;
using System.IO;
using System.Reflection;

// Analysis Services
using Microsoft.AnalysisServices;

// PS 2007 References
using Microsoft.Office.Project.Server.Events;
using Microsoft.Office.Project.Server.Library;

// SharePoint
using System.Web.Services.Protocols;
using Microsoft.SharePoint;

namespace Microsoft.EPM.Events.CubeEvent
{

    #region Cube Admin Events
    public class CubeAdminEventHandler : CubeAdminEventReceiver
    {
        #region Properties
        // Initialize local parameters
        bool logEvents = false;
        bool logFileEveryHour = true;
        string logFolder;

        EventEntry eventLog = new EventEntry();
        AppLog appLog;

        #endregion

        private const string CLASS_NAME = "CubeAdminEventHandler";

        public override void OnCubeProcessed(PSContextInfo contextInfo, CubeAdminPostCubeProcessEventArgs e)
        {
            base.OnCubeProcessed(contextInfo, e);

            #region Local Variables

            string exceptionMsg = null;

            SPSite spSite = new SPSite(contextInfo.SiteGuid);

            #endregion

            try
            {
                #region STEP 0 - Load event handler settings from configuration file

                LoadSettings();

                if (logEvents)
                    appLog = new AppLog(spSite.HostName.ToUpper(), spSite.ServerRelativeUrl, logFolder, logFileEveryHour);

                appLog.WriteLine(string.Format("STEP 0 - Start event override for Cube Built on server {0} for database {1}", e.ServerName, e.DbName));

                #endregion

                // Process AS to read saved roles
                ReadOLAPRoles(e.ServerName, e.DbName);

                #region STEP 6 - Handle Exceptions and logging
            }
            catch (SoapException ex)
            {
                exceptionMsg = GeneralHelper.HandleSoapException(ex);
                eventLog.WriteEvent("FAILED to execute custom Cube Admin event OnProcessed, due to a SoapException: " + exceptionMsg);
            }
            catch (Exception ex)
            {
                exceptionMsg = ex.Message;
                eventLog.WriteEvent("FAILED to execute custom Cube Admin event OnProcessed, due to an Exception: " + exceptionMsg);
            }
            finally
            {
                if (logEvents)
                {
                    if (exceptionMsg != null)
                    {
                        appLog.WriteLine(string.Format("FAILED to execute custom Cube Admin event OnProcessed, due to an exception: {0}", exceptionMsg));
                        appLog.WriteLine(string.Format("CubeAdmin : {0}; ", spSite.ServerRelativeUrl));
                    }
                    else
                        appLog.WriteLine("SUCCESSFULY executed custom Cube Admin event OnProcessed");

                    appLog.Close();
                }
            }
                #endregion

        }

        public override void OnCubeBuilding(PSContextInfo contextInfo, CubeAdminPreCubeBuildEventArgs e)
        {
            base.OnCubeBuilding(contextInfo, e);

            #region Local Variables

            string exceptionMsg = null;

            SPSite spSite = new SPSite(contextInfo.SiteGuid);

            #endregion

            try
            {
                #region STEP 0 - Load event handler settings from configuration file

                LoadSettings();

                if (logEvents)
                    appLog = new AppLog(spSite.HostName.ToUpper(), spSite.ServerRelativeUrl, logFolder, logFileEveryHour);

                appLog.WriteLine(string.Format("STEP 0 - Start event override for Cube Building on server {0} for database {1}", e.ServerName, e.DbName));

                #endregion

                // Process AS to saves roles
                SaveOLAPRoles(e.ServerName, e.DbName);

                #region STEP 6 - Handle Exceptions and logging
            }
            catch (SoapException ex)
            {
                exceptionMsg = GeneralHelper.HandleSoapException(ex);
                eventLog.WriteEvent("FAILED to execute custom Cube Admin event OnBuilding, due to a SoapException: " + exceptionMsg);
            }
            catch (Exception ex)
            {
                exceptionMsg = ex.Message;
                eventLog.WriteEvent("FAILED to execute custom Cube Admin event OnBuilding, due to an Exception: " + exceptionMsg);
            }
            finally
            {
                if (logEvents)
                {
                    if (exceptionMsg != null)
                    {
                        appLog.WriteLine(string.Format("FAILED to execute custom Cube Admin event OnBuilding, due to an exception: {0}", exceptionMsg));
                        appLog.WriteLine(string.Format("CubeAdmin : {0}; ", spSite.ServerRelativeUrl));
                    }
                    else
                        appLog.WriteLine("SUCCESSFULY executed custom Cube Admin event OnBuilding");

                    appLog.Close();
                }
            }
        #endregion

        }

#region Private Methods

        private void LoadSettings()
        {
            // Load settings from Microsoft.Office.Project.Server.Eventing.exe.config
            try
            {
                logEvents = bool.Parse(ConfigurationManager.AppSettings.Get("LogAdminCubeEvent"));
                logFileEveryHour = bool.Parse(ConfigurationManager.AppSettings.Get("LogFileEveryHour"));
                logFolder = ConfigurationManager.AppSettings.Get("LogFolderPath").Trim();
            }
            catch (Exception e)
            {
                eventLog.WriteEvent(string.Format("Could not load settings from Microsoft.Office.Project.Server.Eventing.exe.config: {0}", e.Message));
            }
        }

        private void SaveOLAPRoles(string serverName, string databaseName)
        {
            Stopwatch timeToProcess = new Stopwatch();
            timeToProcess.Start();
            bool success = false;
            string msg = string.Empty;
            OlapRolesMgr mgrOlapRoles = new OlapRolesMgr();
            try
            {
                // --------------------------------------------
                // STEP 1: Connect to Analysis Services server.
                // --------------------------------------------
                Server asServer = new Server(); // To be checked with named instance..
                asServer.Connect(serverName);
                if (logEvents)
                {
                    appLog.WriteLine(string.Format("Connected to Database {0} on Server {1}", databaseName, serverName));
                }

                // --------------------------------------------
                // STEP 2: Locate the necessary OLAP objects.
                // --------------------------------------------
                Database p12Database = asServer.Databases.GetByName(databaseName); // this throws exception if the database is not found
                RoleCollection dbRoles = p12Database.Roles;

                // --------------------------------------------
                // STEP 3: Process the roles.
                // --------------------------------------------
                mgrOlapRoles.SaveToFile(serverName, databaseName, p12Database, dbRoles, logFolder, logEvents, appLog);

                success = true;
            }
            catch (Exception e)
            {
                msg = "FAILED to get AS OLAP Roles, exception: " + e.Message;
            }

            timeToProcess.Stop();
            if (success)
                WriteEvent("SUCCESSFULY got AS OLAP Roles, Time= " + timeToProcess.Elapsed.ToString(), EventLogEntryType.Information, 9999);
            else
                WriteEvent(msg + " , Time= " + timeToProcess.Elapsed.ToString(), EventLogEntryType.Error, 9999);

        }

        private void ReadOLAPRoles(string serverName, string databaseName)
        {
            Stopwatch timeToProcess = new Stopwatch();
            timeToProcess.Start();
            bool success = false;
            string msg = string.Empty;
            OlapRolesMgr mgrOlapRoles = new OlapRolesMgr();
            try
            {

                // --------------------------------------------
                // STEP 1: Connect to Analysis Services server.
                // --------------------------------------------
                Server asServer = new Server(); // To be checked with named instance..
                asServer.Connect(serverName);
                if (logEvents)
                {
                    appLog.WriteLine(string.Format("Connected to Database {0} on Server {1}", databaseName, serverName));
                }

                // --------------------------------------------
                // STEP 2: Locate the necessary OLAP objects.
                // --------------------------------------------
                Database p12Database = asServer.Databases.GetByName(databaseName); // this throws exception if the database is not found
                RoleCollection dbRoles = p12Database.Roles;

                // --------------------------------------------
                // STEP 3: Read and Set the new roles.
                // --------------------------------------------
                mgrOlapRoles.ReadFromFile(serverName, databaseName, p12Database, dbRoles, logFolder, logEvents, appLog);
                
                success = true;
            }
            catch (Exception e)
            {
                msg = "FAILED to get AS OLAP Roles, exception: " + e.Message;
            }

            timeToProcess.Stop();
            if (success)
            {
                WriteEvent("SUCCESSFULY got AS OLAP Roles, Time= " + timeToProcess.Elapsed.ToString(), EventLogEntryType.Information, 9999);
                appLog.WriteLine("SUCCESSFULY got AS OLAP Roles, Time= " + timeToProcess.Elapsed.ToString());
            }
            else
            {
                WriteEvent(msg + " , Time= " + timeToProcess.Elapsed.ToString(), EventLogEntryType.Error, 9999);
                appLog.WriteLine("ERROR getting AS OLAP Roles, Time= " + timeToProcess.Elapsed.ToString() + msg);
            }
        }

        private void WriteEvent(string msg, EventLogEntryType logEntryType, int eventId)
        {
            EventLog myLog = new EventLog();
            myLog.Source = "Project Event Handler";

            string message = msg;
            myLog.WriteEntry(msg, logEntryType, eventId);
        }

        #endregion
    }
    #endregion

    #region Output Methods
    public class Helper
    {
        static internal void WriteOutput(object contextInfo, object eventArgs, string className, string eventName)
        {
            // Create an EventLog instance and assign its source.
            EventLog myLog = new EventLog();
            myLog.Source = "PSI Event Logger";

            try
            {
                string logEntry = "Event=" + className + "." + eventName + "\n";

                logEntry = logEntry + BuildString(contextInfo);
                logEntry = logEntry + BuildString(eventArgs);

                myLog.WriteEntry(logEntry, EventLogEntryType.Information);
            }
            catch (Exception err)
            {
                myLog.WriteEntry("Error in PSI event logger, " + className + "." + eventName + " event.  " + err.Message, EventLogEntryType.Information);
            }
        }

        static internal string BuildString(object obj)
        {
            string logEntry = "";

            const BindingFlags bf = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.NonPublic;
            foreach (PropertyInfo pi in obj.GetType().GetProperties(bf))
            {
                if (pi.GetValue(obj, null) == null)
                {
                    logEntry = logEntry + pi.Name + "=null\n";
                }
                else
                {
                    logEntry = logEntry + pi.Name + "=" + pi.GetValue(obj, null).ToString() + "\n";
                }
            }
            return logEntry;
        }
    }
    #endregion
}
